grammar Gxc;
options {   
      language = CSharp2;
      backtrack=true;
      memoize=true;
      k=2;
      output=AST;
      ASTLabelType=CommonTree;
}
tokens {
  CONDITIONAL_EXPRESSION;
  FUNCTION_CALL;
  ASSIGNMENT;
  CODE_BLOCK;
  CASE;
  DEFAULT_CASE;
  VARIABLE;
  UNARY_INCREMENT;
  UNARY_DECREMENT;
  
}
scope Symbols {
	HashSet<string> types;
}
@header {
using System.Collections.Generic;

}
@parser::namespace { gxc2csharp.Translator.GxcToCTransform } // Or just @namespace { ... }

@lexer::namespace { gxc2csharp.Translator.GxcToCTransform }

@members {
	bool isTypeName(String name) {
    		
    		return true;
    	}
  
}
// These are imaginary tokens that will serve as parent nodes
// for grouping other tokens in our AST.



/**
 * A GXC file is a bunch of declaration and then a code block 
 *
 */

translation_unit
scope Symbols; // entire file is a scope
@init {
  $Symbols::types = new HashSet<string>();
  GxVariables = new Dictionary<string, string> ();
  PInvokeFunctions = new  List<string> ();
  Operators = new Dictionary<string, int>();
}
	: gx_meta_declarations+ declaration* compound_statement
	;

// ---- GX META DATA RULES ----- 

gx_meta_declarations!
   :gx_meta_data! gx_resource*
   ;
//these can only occur once in a file 
gx_meta_data!
   : 'NAME' '=' STRING_LITERAL  {GxName = input.LT(-1).Text;}
   | 'VERSION' '=' STRING_LITERAL {GxVersion= input.LT(-1).Text;}
   | 'DESCRIPTION' '=' STRING_LITERAL {GxDescription= input.LT(-1).Text;}
   ;

//a gx can have many resources but all need to declared before 
// any variable declaration
 gx_resource!
   :'RESOURCE' '=' STRING_LITERAL {ReadInGrcFile($STRING_LITERAL.Text);}
   ;
  
// ---- GX DECLARATION RULES ----- 
declaration
	:type_declaration! 
	|function_declaration!
	|variable_declaration!   
	;
	
type_declaration
scope {
bool typedef;
}
   : 'typedef' IDENTIFIER (',' IDENTIFIER)* ';' 
   ;
function_declaration
scope {
 string dll;
 List<string>argTypes;
 string return_type;
}
@init {
$function_declaration::argTypes = new List<string>();
}
   : dll_specifier* returntype_specifier IDENTIFIER '(' func_declaration_arugments* ')' ';' {AddFunctionDeclaration($IDENTIFIER.Text,$function_declaration::dll,$function_declaration::return_type,$function_declaration::argTypes);}
   ;
   
   
   
variable_declaration
scope {
 List<string> variables;
 string type;
 int size;
}
@init {
  $variable_declaration::variables = new List<string>();
  $variable_declaration::size=1;
}
   : type_specifier identifier_list ';'  {AddVariables($variable_declaration::type,$variable_declaration::size, $variable_declaration::variables);}
   ;
 returntype_specifier
   : 'void' {$function_declaration::return_type="void";}
	| 'int'  {$function_declaration::return_type="int";}
	| 'real' {$function_declaration::return_type="real";}
	| 'string' {$function_declaration::return_type="string";}
	| type_id  {$function_declaration::return_type=input.LT(-1).Text;}
	;

   
 func_declaration_specifiers
	: 'void' {$function_declaration::argTypes.Add("void");}
	| 'int'  {$function_declaration::argTypes.Add("int");}
	| 'real' {$function_declaration::argTypes.Add("real");}
	| 'string' {$function_declaration::argTypes.Add("string");}
	| type_id  {$function_declaration::argTypes.Add(input.LT(-1).Text);}
	            
	| 'var' 'void' {$function_declaration::argTypes.Add("ref void/*really!!!!*/");}
	| 'var' 'int'  {$function_declaration::argTypes.Add("ref int");}
	| 'var' 'real' {$function_declaration::argTypes.Add("ref real");}
	| 'var' 'string' {$function_declaration::argTypes.Add("ref string");}
	| 'var' type_id  {$function_declaration::argTypes.Add("ref "+ input.LT(-1).Text);}
	;

dll_specifier
   :'[' IDENTIFIER ']' {$function_declaration::dll=$IDENTIFIER.Text;}
   ;

func_declaration_arugments
   :func_declaration_specifiers (',' func_declaration_specifiers)*
   ;
variable_type_specifier
	: 'void' {$variable_declaration::type="void";}
	| 'int'  (  '('! DECIMAL_LITERAL{$variable_declaration::size=int.Parse($DECIMAL_LITERAL.Text);} ')'! )* {$variable_declaration::type="int";}
	| 'real' (  '('! DECIMAL_LITERAL {$variable_declaration::size=int.Parse($DECIMAL_LITERAL.Text);}')'!   )*{$variable_declaration::type="real";}
	| 'string'  (  '('! DECIMAL_LITERAL {$variable_declaration::size=int.Parse($DECIMAL_LITERAL.Text);} ')'!   )*{$variable_declaration::type="string";}
	| type_id  {$variable_declaration::type="string";}
	;
	
type_specifier
	: 'void' 
	| 'int'  (  '('! DECIMAL_LITERAL')'! )* 
	| 'real' (  '('! DECIMAL_LITERAL ')'!   )*
	| 'string'  (  '('! DECIMAL_LITERAL ')'!   )*
	| type_id  
	;


type_id
    :   {isTypeName(input.LT(1).Text)}? IDENTIFIER
    ;




identifier_list
	: identifier  (','! identifier)*  
	; 
identifier

   : IDENTIFIER {
                      $variable_declaration::variables.Add($IDENTIFIER.Text);
                }
   ;
 
//---- END GX Declaration RULES 

 

// E x p r e s s i o n s

argument_expression_list
	:   assignment_expression (','! assignment_expression)*
	;

additive_expression
	: (multiplicative_expression) ('+'^ multiplicative_expression | '-'^ multiplicative_expression)*
	;

multiplicative_expression
	: (cast_expression) ('*'^ cast_expression | '/'^ cast_expression | '%'^ cast_expression)*
	;

cast_expression
	: '('! type_specifier ')'! cast_expression
	| unary_expression
	;

unary_expression
	: postfix_expression
	| '++'{IncrementOperatorCount("_++");} unary_expression
	| '--'{IncrementOperatorCount("_--");} unary_expression
	| unary_operator cast_expression
	| 'sizeof' unary_expression
	| 'sizeof' '('! type_specifier ')'!
	;

postfix_expression
	:	primary_expression -> ^(VARIABLE primary_expression)
	|	primary_expression '[' expression ']'{IncrementOperatorCount("[]");} 
	|	primary_expression '++'{IncrementOperatorCount("++");} -> ^(ASSIGNMENT ^(VARIABLE primary_expression) UNARY_INCREMENT)
	|	primary_expression '--'{IncrementOperatorCount("--");}  -> ^(ASSIGNMENT ^(VARIABLE primary_expression) UNARY_DECREMENT)
	|   primary_expression '(' argument_expression_list? ')' ->^(FUNCTION_CALL ^(primary_expression argument_expression_list?))
	;

unary_operator
	: '&'{IncrementOperatorCount("&");}
	| '*'{IncrementOperatorCount("*");}
	| '+'{IncrementOperatorCount("+");}
	| '-'{IncrementOperatorCount("-");}
	| '~'{IncrementOperatorCount("~");}
	| '!'{IncrementOperatorCount("!");}
	;

primary_expression
	: IDENTIFIER
	| constant
	| '('! expression ')'!
	;

constant
    :   HEX_LITERAL
    |   OCTAL_LITERAL
    |   DECIMAL_LITERAL
    | '(' 'int' ')'  DECIMAL_LITERAL //geosoft  from rc file (( int ) 0)
    |	CHARACTER_LITERAL
    |	STRING_LITERAL
    |   FLOATING_POINT_LITERAL
    ;

/////

expression
	: assignment_expression (','!{IncrementOperatorCount(",");} assignment_expression)*
	;

constant_expression
	: conditional_expression
	;

assignment_expression
	: lvalue assignment_operator assignment_expression -> ^(ASSIGNMENT lvalue assignment_expression)
	| conditional_expression
	;
	
lvalue
	:	unary_expression
	;

assignment_operator
	: '='{IncrementOperatorCount("=");}
	| '*='{IncrementOperatorCount("*=");}
	| '/='{IncrementOperatorCount("/=");}
	| '%='{IncrementOperatorCount("mod=");}
	| '+='{IncrementOperatorCount("+=");}
	| '-='{IncrementOperatorCount("-=");}
	| '<<='{IncrementOperatorCount("<<=");}
	| '>>='{IncrementOperatorCount(">>=");}
	| '&='{IncrementOperatorCount("&=");}
	| '^='{IncrementOperatorCount("^=");}
	| '|='{IncrementOperatorCount("|=");}
	;

conditional_expression
	: logical_or_expression ('?' expression ':' conditional_expression)?
	;

logical_or_expression
	: logical_and_expression ('||'^{IncrementOperatorCount("||");} logical_and_expression)*
	;

logical_and_expression
	: inclusive_or_expression ('&&'^{IncrementOperatorCount("&&");} inclusive_or_expression)*
	;

inclusive_or_expression
	: exclusive_or_expression ('|'^{IncrementOperatorCount("|");} exclusive_or_expression)*
	;

exclusive_or_expression
	: and_expression ('^'^{IncrementOperatorCount("^");} and_expression)*
	;

and_expression
	: equality_expression ('&'^{IncrementOperatorCount("&");} equality_expression)*
	;
equality_expression
	: relational_expression (('=='{IncrementOperatorCount("==");}|'!='{IncrementOperatorCount("!=");})^ relational_expression)*
	;

relational_expression
	: shift_expression (('<'{IncrementOperatorCount("<");}|'>'{IncrementOperatorCount(">");}|'<='{IncrementOperatorCount("<=");}|'>='{IncrementOperatorCount(">=");})^ shift_expression)*
	;

shift_expression
	: additive_expression (('<<'{IncrementOperatorCount("<<");}|'>>'{IncrementOperatorCount(">>");})^ additive_expression)*
	;

// S t a t e m e n t s

statement
	: labeled_statement 
	| compound_statement
	| expression_statement
	| selection_statement {CyclomaticComplexity++;}
	| iteration_statement {CyclomaticComplexity++;}
	| jump_statement
	| comment_statement
	;


labeled_statement
	: IDENTIFIER ':' statement 
	| 'case'{CyclomaticComplexity++;IncrementOperatorCount("case");} constant_expression ':' statement   -> ^(CASE constant_expression ^(CODE_BLOCK) statement?)
	| 'default'{CyclomaticComplexity++;IncrementOperatorCount("default");} ':' statement-> ^(DEFAULT_CASE ^(CODE_BLOCK) statement?)
	;

compound_statement
scope Symbols; // blocks have a scope of symbols
@init {
  $Symbols::types = new HashSet<string>();
}
	: '{'! declaration* statement_list? '}'!
	;

statement_list
	: statement+
	;

expression_statement
	: ';'!
	| expression ';'! 
	;

selection_statement
	: IF{IncrementOperatorCount("if");}   '(' expression ')' s1=statement 
	   (options {k=1; backtrack=false;}:ELSE{IncrementOperatorCount("else");} s2 = statement -> ^(IF ^(CONDITIONAL_EXPRESSION expression) ^(CODE_BLOCK $s1) ^(ELSE ^(CODE_BLOCK $s2)))
	   | -> ^(IF ^(CONDITIONAL_EXPRESSION expression ) $s1 ) )?  
	| SWITCH {IncrementOperatorCount("switch");}   '(' expression ')' statement    -> ^(SWITCH ^(CONDITIONAL_EXPRESSION expression) statement)  
	;

iteration_statement
	: WHILE {IncrementOperatorCount("while");} '(' expression ')' statement -> ^(WHILE ^(CONDITIONAL_EXPRESSION ^(expression statement) ) )
	| DO {IncrementOperatorCount("do");} statement WHILE '(' expression ')' ';' ->^(DO statement ^(CONDITIONAL_EXPRESSION expression))
	| FOR {IncrementOperatorCount("for");} '(' init=expression_statement cond=expression_statement inc = expression? ')' statement -> ^(FOR $init ^(CONDITIONAL_EXPRESSION ^($cond statement)) $inc?)
	;

jump_statement
	: 'goto' IDENTIFIER ';'!
	| CONTINUE {IncrementOperatorCount("continue");}  ';' -> ^(CONTINUE)
	| BREAK {IncrementOperatorCount("break");} ';'  -> ^(BREAK)
	;
// We read in the comments as they contain very valuable information about the code.
comment_statement
   :LINE_COMMENT{LinesOfComments++;}
   |COMMENT  {CountBlockCommentLines($COMMENT.Text);}
   ;

/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/
IF: 'if';
ELSE: 'else';
SWITCH:'switch';
WHILE:'while';
DO:'do';
FOR:'for';
CONTINUE:'continue';
BREAK:'break'	;

IDENTIFIER
	:	LETTER (LETTER|'0'..'9')*
	;
	
fragment
LETTER
	:	'$'
	|	'A'..'Z'
	|	'a'..'z'
	|	'_'
	;

CHARACTER_LITERAL
    :   '\'' ( EscapeSequence | ~('\''|'\\') ) '\''
    ;

//all gx strings are multiline 

STRING_LITERAL
    //:  '"' (options {greedy=false;} : .)* '"'  
    : '"' ( '\\' . | ~('\\'|'"') )* '"' // double quoted string
    ;

HEX_LITERAL : '0' ('x'|'X') HexDigit+ IntegerTypeSuffix? ;

DECIMAL_LITERAL : ('0' | '1'..'9' '0'..'9'*) IntegerTypeSuffix? ;

OCTAL_LITERAL : '0' ('0'..'7')+ IntegerTypeSuffix? ;

fragment
HexDigit : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
IntegerTypeSuffix
	:	('u'|'U')? ('l'|'L')
	|	('u'|'U')  ('l'|'L')?
	;

FLOATING_POINT_LITERAL
    :   ('0'..'9')+ '.' ('0'..'9')* Exponent? FloatTypeSuffix?
    |   '.' ('0'..'9')+ Exponent? FloatTypeSuffix?
    |   ('0'..'9')+ Exponent FloatTypeSuffix?
    |   ('0'..'9')+ Exponent? FloatTypeSuffix
	;

fragment
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

fragment
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;

fragment
EscapeSequence
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   OctalEscape
    ;

fragment
OctalEscape
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UnicodeEscape
    :   '\\' 'u' HexDigit HexDigit HexDigit HexDigit
    ;

WS  :  (' '|'\t'|'\u000C') {$channel=HIDDEN;}
	|('\r'|'\n'|'\r\n') {if(this.state.tokenStartCharPositionInLine>0)
							LinesOfCode++;
								$channel=HIDDEN;
					    }
    ;

COMMENT
    :   '/*' ( options {greedy=false;} : . )* '*/'
    ;

LINE_COMMENT
    : '//' ~('\n'|'\r')* '\r'? '\n' 
    ;

// ignore #line info for now
LINE_COMMAND 
    : '#' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;
STRAY_BAKSLASH
     :'\\'  {$channel=HIDDEN;}
     ;
     
